<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>第7章 Vue Router 路由搭建应用 | 深入理解Vue.js实战</title>
    <meta name="generator" content="VuePress 1.9.7">
    
    <meta name="description" content="作者：被删">
    
    <link rel="preload" href="/vue-ebook/assets/css/0.styles.2728a2da.css" as="style"><link rel="preload" href="/vue-ebook/assets/js/app.a856ee15.js" as="script"><link rel="preload" href="/vue-ebook/assets/js/2.2a4d101b.js" as="script"><link rel="preload" href="/vue-ebook/assets/js/3.9083e877.js" as="script"><link rel="preload" href="/vue-ebook/assets/js/21.15bf7d53.js" as="script">
    <link rel="stylesheet" href="/vue-ebook/assets/css/0.styles.2728a2da.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/vue-ebook/" class="home-link router-link-active"><!----> <span class="site-name">深入理解Vue.js实战</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/vue-ebook/" class="nav-link">概述</a></div><div class="nav-item"><a href="/vue-ebook/vue-ebook/" class="nav-link router-link-active">内容</a></div> <a href="https://github.com/godbasin/vue-ebook" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/vue-ebook/" class="nav-link">概述</a></div><div class="nav-item"><a href="/vue-ebook/vue-ebook/" class="nav-link router-link-active">内容</a></div> <a href="https://github.com/godbasin/vue-ebook" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><section class="sidebar-group depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>前言</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/0.html" class="sidebar-link">前端框架的出现</a></li></ul></section></li><li><section class="sidebar-group depth-0" style="padding-top:10px;"><div class="kitty-main" data-v-2b653b36><span class="stand" data-v-2b653b36></span> <div class="cat" data-v-2b653b36><div class="body" data-v-2b653b36></div> <div class="head" data-v-2b653b36><div class="ear" data-v-2b653b36></div> <div class="ear" data-v-2b653b36></div></div> <div class="face" data-v-2b653b36><div class="nose" data-v-2b653b36></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div></div> <div class="tail-container" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36></div></div></div></div></div></div></div></div></div></div> <p class="sidebar-heading open"><span>第一部分 Vue快速入门</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/1.html" class="sidebar-link">第1章 Vue 框架介绍</a></li><li><a href="/vue-ebook/vue-ebook/2.html" class="sidebar-link">第2章 Vue 环境快速搭建</a></li><li><a href="/vue-ebook/vue-ebook/3.html" class="sidebar-link">第3章 Vue 基础介绍</a></li><li><a href="/vue-ebook/vue-ebook/4.html" class="sidebar-link">第4章 Vue 组件的使用</a></li><li><a href="/vue-ebook/vue-ebook/5.html" class="sidebar-link">第5章 常用指令和自定义指令</a></li><li><a href="/vue-ebook/vue-ebook/6.html" class="sidebar-link">第6章 Vue 动画</a></li><li><a href="/vue-ebook/vue-ebook/7.html" aria-current="page" class="active sidebar-link">第7章 Vue Router 路由搭建应用</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/vue-ebook/vue-ebook/7.html#_7-1-前端路由" class="sidebar-link">7.1 前端路由</a></li><li class="sidebar-sub-header"><a href="/vue-ebook/vue-ebook/7.html#_7-2-快速接入-vue-router" class="sidebar-link">7.2 快速接入 Vue Router</a></li><li class="sidebar-sub-header"><a href="/vue-ebook/vue-ebook/7.html#_7-3-vue-router-进阶" class="sidebar-link">7.3 Vue Router 进阶</a></li></ul></li><li><a href="/vue-ebook/vue-ebook/8.html" class="sidebar-link">第8章 实战：Todo List 从组件到应用</a></li></ul></section></li><li><section class="sidebar-group depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>第二部分 Vue的正确使用方式</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/9.html" class="sidebar-link">第9章 思维转变与大型项目管理</a></li><li><a href="/vue-ebook/vue-ebook/10.html" class="sidebar-link">第10章 如何正确地进行抽象</a></li><li><a href="/vue-ebook/vue-ebook/11.html" class="sidebar-link">第11章 全局数据管理与 Vuex</a></li><li><a href="/vue-ebook/vue-ebook/12.html" class="sidebar-link">第12章 实战：三天开发一个管理端</a></li><li><a href="/vue-ebook/vue-ebook/13.html" class="sidebar-link">第13章 实战：表单配置化实现</a></li><li><a href="/vue-ebook/vue-ebook/14.html" class="sidebar-link">第14章 实战：使用 Webpack 或 Vue CLI 搭建多页应用</a></li><li><a href="/vue-ebook/vue-ebook/15.html" class="sidebar-link">第15章 Vue 周边拓展</a></li><li><a href="/vue-ebook/vue-ebook/16.html" class="sidebar-link">第16章 关于 Vue 3.0</a></li></ul></section></li><li><section class="sidebar-group depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>后记</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/99.html" class="sidebar-link">关于框架选型</a></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="第7章-vue-router-路由搭建应用"><a href="#第7章-vue-router-路由搭建应用" class="header-anchor">#</a> 第7章 Vue Router 路由搭建应用</h1> <blockquote><p>本章节相关代码存放在<a href="https://github.com/godbasin/vue-ebook/tree/vue-sourcecode/7" target="_blank" rel="noopener noreferrer">Github<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>中。</p></blockquote> <p>页面的跳转、局部内容的刷新是 web 应用中使用最多的场景。想象一下，如果我们只刷新了页面的内容，但是 URL 并没有改变，当用户刷新当前页面的时候，原先的内容会丢失，需要重新操作进入到对应的页面中，这是比较糟糕的一种体验。所以我们可以把页面的内容匹配到对应的路由信息中，即使是 ctrl+F5 这样的强制刷新，URL 信息也不会丢，用户依然可以快速恢复原先的页面浏览信息，这也是我们要设计和使用路由的很重要的原因。</p> <p>本章主要介绍如何使用 Vue Router 来管理路由，包括路由的安装和配置、跳转、路由鉴权等。了解一个工具如何使用的同时，更应该了解工具的实现、为什么需要这样来使用。关于 Vue Router 的实现，其实可以从前端路由的出现和常见的实现方式来说起。</p> <h2 id="_7-1-前端路由"><a href="#_7-1-前端路由" class="header-anchor">#</a> 7.1 前端路由</h2> <p>在过去，服务端处理来自浏览器的请求时，要根据不同的 URL 路由拼接出对应的视图页面，通过 Http 返回给浏览器进行解析渲染。使用这种方式加载页面，整个页面都需要重新加载，导致体验不够友好。随着几年前 AngularJS、React、Ember 等这些框架的出现，Web 应用也随之出现。<a href="/vue-ebook/vue-ebook/1.html">第1章</a>中我们讲了页面的局部刷新，Web 应用则是使用了这种局部刷新的能力，在路由信息变更的时候进行局部页面内容的刷新（而不是重新加载一个完整的页面），从而获取更好的体验。</p> <h3 id="_7-1-1-路由实现"><a href="#_7-1-1-路由实现" class="header-anchor">#</a> 7.1.1 路由实现</h3> <p>路由相关的功能，大多数都是基于基本的 History API、Location API 这些浏览器提供的 API 能力封装的，所以这里我们先介绍和路由有关的一些 API。前端路由的实现，一般包括两种模式：History 模式和 Hash 模式。</p> <h4 id="history-模式"><a href="#history-模式" class="header-anchor">#</a> History 模式</h4> <p>History 的路由模式，依赖了一个关键的属性<strong>window.history</strong>。</p> <p><code>window.history</code>是一个只读属性，用来获取 History 对象的引用。History 对象提供了操作浏览器会话历史（浏览器地址栏中访问的页面，以及当前页面中通过框架加载的页面）的接口，使用<code>window.history</code>我们可以实现以下与路由相关的重要能力：</p> <p>(1) <strong>在 history 中跳转</strong>。<br>
使用<code>window.history.back()</code>、<code>window.history.forward()</code>和<code>window.history.go()</code>方法来完成在用户历史记录中向后和向前的跳转。</p> <p>(2) <strong>添加和修改历史记录中的条目</strong>。<br>
HTML5 引入了<code>history.pushState()</code>和<code>history.replaceState()</code>方法，它们分别可以添加和修改历史记录条目。这两个 API 都会操作浏览器的历史栈，而不会引起页面的刷新。区别在于，<code>pushState()</code>会增加一条新的历史记录，而<code>replaceState()</code>则会替换当前的历史记录：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">/**
 * parameters
 * @state {object} 状态对象 state 是一个 JavaScript 对象，一般是JSON格式的对象字面量
 * @title {string} 可以理解为 document.title，在这里是作为新页面传入参数的
 * @url {string} 该参数定义了增加或改变的历史 URL 记录，可以是相对路径或者绝对路径，url的具体格式可以自定
 */</span>
history<span class="token punctuation">.</span><span class="token function">pushState</span><span class="token punctuation">(</span>state<span class="token punctuation">,</span> title<span class="token punctuation">,</span> url<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 向浏览器历史栈中增加一条记录</span>
history<span class="token punctuation">.</span><span class="token function">replaceState</span><span class="token punctuation">(</span>state<span class="token punctuation">,</span> title<span class="token punctuation">,</span> url<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 替换历史栈中的当前记录</span>
</code></pre></div><p>(3) <strong>监听 popstate 事件</strong>。<br>
当同一个页面在历史记录间切换时，就会产生<code>popstate</code>事件，<code>popstate</code>事件会被传递给<code>window</code>对象，所以页面路由切换通常会与<code>window.onpopstate</code>配合使用。</p> <p>上面介绍的<code>history.pushState()</code>或者<code>history.replaceState()</code>调用不会触发<code>popstate</code>事件，<code>popstate</code>事件只会在浏览器某些行为下触发, 比如点击后退、前进按钮(或者在 JavaScript 中调用<code>history.back()</code>、<code>history.forward()</code>、<code>history.go()</code>方法)。所以我们可以结合<code>popstate</code>事件、<code>pushState()</code>和<code>replaceState()</code>来完成完整的路由监听和修改能力。</p> <p>如果当前处于激活状态的历史记录条目是由<code>history.pushState()</code>方法创建，或者由<code>history.replaceState()</code>方法修改过的, 则<code>popstate</code>事件对象的<code>state</code>属性包含了这个历史记录条目的<code>state</code>对象的一个拷贝。我们来看看示例：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 假如当前网页地址为http://example.com/example.html</span>
window<span class="token punctuation">.</span><span class="token function-variable function">onpopstate</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">alert</span><span class="token punctuation">(</span>
    <span class="token string">&quot;location: &quot;</span> <span class="token operator">+</span> document<span class="token punctuation">.</span>location <span class="token operator">+</span> <span class="token string">&quot;, state: &quot;</span> <span class="token operator">+</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>state<span class="token punctuation">)</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">//绑定事件处理函数</span>
<span class="token comment">//添加并激活一个历史记录条目 http://example.com/example.html?page=1,条目索引为1</span>
history<span class="token punctuation">.</span><span class="token function">pushState</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">page</span><span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">&quot;title 1&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;?page=1&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//添加并激活一个历史记录条目 http://example.com/example.html?page=2,条目索引为2</span>
history<span class="token punctuation">.</span><span class="token function">pushState</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">page</span><span class="token operator">:</span> <span class="token number">2</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">&quot;title 2&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;?page=2&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//修改当前激活的历史记录条目 http://ex..?page=2 变为 http://ex..?page=3,条目索引为3</span>
history<span class="token punctuation">.</span><span class="token function">replaceState</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">page</span><span class="token operator">:</span> <span class="token number">3</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">&quot;title 3&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;?page=3&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

history<span class="token punctuation">.</span><span class="token function">back</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 弹出 &quot;location: http://example.com/example.html?page=1, state: {&quot;page&quot;:1}&quot;</span>
history<span class="token punctuation">.</span><span class="token function">back</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 弹出 &quot;location: http://example.com/example.html, state: null</span>
history<span class="token punctuation">.</span><span class="token function">go</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 弹出 &quot;location: http://example.com/example.html?page=3, state: {&quot;page&quot;:3}</span>
</code></pre></div><h4 id="hash-模式"><a href="#hash-模式" class="header-anchor">#</a> Hash 模式</h4> <p>History 模式需要依赖 HTML5 History API（IE10 以上），以及服务器的配置来支持，所以也有不少的开发者会使用 Hash 模式来管理 Web 应用的路由。而 Hash 模式主要依赖 Location 对象的 hash 属性（<code>location.hash</code>）和<code>hashchange</code>事件，我们来分别看一下。</p> <p>(1) <strong>Location 对象</strong>。</p> <p><code>window.location</code>用来获取 Location 对象的引用。Location 对象存储在 Window 对象的 Location 属性中，表示当前 Web 地址。Location 对象提供的属性如表 7-1：</p> <p>表 7-1 Location 属性</p> <table><thead><tr><th>Location 属性</th> <th>描述</th> <th>示例，<code>https://www.test.com/en-US/search?q=URL#search-results</code></th></tr></thead> <tbody><tr><td>hash</td> <td>设置或返回从井号(#)开始的 URL（锚）</td> <td><code>#search-results</code></td></tr> <tr><td>host</td> <td>设置或返回主机名和当前 URL 的端口号</td> <td><code>www.test.com</code></td></tr> <tr><td>hostname</td> <td>设置或返回当前 URL 的主机名</td> <td><code>www.test.com</code></td></tr> <tr><td>href</td> <td>设置或返回完整的 URL</td> <td><code>https://www.test.com/en-US/search?q=URL#search-results</code></td></tr> <tr><td>pathname</td> <td>设置或返回当前 URL 的路径部分</td> <td><code>/en-US/search</code></td></tr> <tr><td>port</td> <td>设置或返回当前 URL 的端口号</td> <td>默认 80 端口</td></tr> <tr><td>protocol</td> <td>设置或返回当前 URL 的协议</td> <td><code>https:</code></td></tr> <tr><td>search</td> <td>设置或返回从问号(?)开始的 URL（查询部分）</td> <td><code>?q=URL</code></td></tr></tbody></table> <p><code>location.hash</code>的设置和获取，并不会造成页面重新加载，利用这一点，我们可以记录页面关键信息的同时，提升页面体验。</p> <p>(2) <strong>hashchange 事件</strong>。</p> <p>当一个窗口的 hash 改变时就会触发<code>hashchange</code>事件。<code>hashchange</code>事件对象有两个属性，<code>newURL</code>为当前页面新的 URL，<code>oldURL</code>为当前页面旧的 URL。</p> <p>Hash 模式通过<code>window.onhashchange</code>监听基于 hash 的路由变化，来进行页面更新处理的。部分浏览器不支持<code>onhashchange</code>事件，我们可以自行使用定时器检测和触发的方式来进行兼容：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">window</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 如果浏览器原生支持该事件,则退出</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token string">&quot;onhashchange&quot;</span> <span class="token keyword">in</span> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>body<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">var</span> location <span class="token operator">=</span> window<span class="token punctuation">.</span>location<span class="token punctuation">,</span>
    oldURL <span class="token operator">=</span> location<span class="token punctuation">.</span>href<span class="token punctuation">,</span>
    oldHash <span class="token operator">=</span> location<span class="token punctuation">.</span>hash<span class="token punctuation">;</span>
  <span class="token comment">// 每隔100ms检测一下location.hash是否发生变化</span>
  <span class="token function">setInterval</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">var</span> newURL <span class="token operator">=</span> location<span class="token punctuation">.</span>href<span class="token punctuation">,</span>
      newHash <span class="token operator">=</span> location<span class="token punctuation">.</span>hash<span class="token punctuation">;</span>
    <span class="token comment">// 如果hash发生了变化,且绑定了处理函数...</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>newHash <span class="token operator">!=</span> oldHash <span class="token operator">&amp;&amp;</span> <span class="token keyword">typeof</span> window<span class="token punctuation">.</span>onhashchange <span class="token operator">===</span> <span class="token string">&quot;function&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 执行事件触发</span>
      window<span class="token punctuation">.</span><span class="token function">onhashchange</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">&quot;hashchange&quot;</span><span class="token punctuation">,</span>
        <span class="token literal-property property">oldURL</span><span class="token operator">:</span> oldURL<span class="token punctuation">,</span>
        <span class="token literal-property property">newURL</span><span class="token operator">:</span> newURL
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      oldURL <span class="token operator">=</span> newURL<span class="token punctuation">;</span>
      oldHash <span class="token operator">=</span> newHash<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><h3 id="_7-1-2-路由的实现原理"><a href="#_7-1-2-路由的实现原理" class="header-anchor">#</a> 7.1.2 路由的实现原理</h3> <p>路由现在也成为前端框架里一个最基本的能力了，虽然实现上并没有太大的难度，但却是单页应用不可或缺的部分。因为实现的简单和相似，所以大家基本都直接使用框架自带的 router，像 ngRouter/vue-router/react-router 等等。</p> <h4 id="路由实现步骤"><a href="#路由实现步骤" class="header-anchor">#</a> 路由实现步骤</h4> <p>一般来说，路由都是通过 History API、Location API 和相关的事件监听实现。我们以 Hash 模式为例，如果要实现路由变化的时候加载相应的内容，步骤大概分为三步：<br>
(1) 设置监听器，监听<code>popstate</code>或者<code>hashchange</code>事件。<br>
(2) 通过 hash（location.href.hash）获取当前的路由位置。<br>
(3) 根据当前匹配路径，判断后加载对应模块。</p> <p>以上是简单的实现，像 Vue Router 这种工具库则是将上面的实现封装成通用的能力，同时提供了简单的事件监听和通知之外的一些更高级的能力，例如<code>&lt;router-link&gt;</code>、<code>&lt;router-view&gt;</code>这种与渲染层结合解析和处理的能力，以及路由的钩子、匹配和映射、懒加载打包等这种能力。我们来从使用的角度看看 Vue Router 的能力吧。</p> <h4 id="vue-router-实现"><a href="#vue-router-实现" class="header-anchor">#</a> Vue Router 实现</h4> <p>前面介绍了 History 模式和 Hash 模式两种，我们常见的路由库也都同样会提供这两种方式，Vue Router 也不例外。Vue Router 甚至兼容了 Node.js 服务端的情况，它提供的路由模式包括三种：</p> <p>表 7-2 Vue Router 路由模式</p> <table><thead><tr><th>路由模式</th> <th>说明</th> <th>示例</th></tr></thead> <tbody><tr><td><code>hash</code></td> <td>使用 URL hash 值来作路由（支持所有浏览器，包括不支持 HTML5 History Api 的浏览器）</td> <td><code>a.com/#/pageone</code></td></tr> <tr><td><code>history</code></td> <td>充分利用<code>history.pushState</code> API 来完成 URL 跳转而无须重新加载页面</td> <td><code>a.com/pageone</code></td></tr> <tr><td><code>abstract</code></td> <td>支持所有 JavaScript 运行环境，如 Node.js 服务器端（在 Node.js 端会自动设置该模式为默认值）<br>如果发现没有浏览器的 API，路由会自动强制进入这个模式</td> <td>-</td></tr></tbody></table> <p>需要注意的地方是，History 模式需要依赖 HTML5 History API（IE10 以上）和服务器配置。你要在服务端增加一个覆盖所有情况的候选资源：如果 URL 匹配不到任何静态资源，则应该返回同一个<code>index.html</code>页面，这个页面就是你 app 依赖的页面。所以如果觉得 hash 片段可以接受（有些人会觉得太丑了无法接受），可以优先选择 Hash 模式来进行开发，简单方便。</p> <p>我们看看 Vue Router 中的 History 对象：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// HTML5 History</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">HTML5History</span> <span class="token keyword">extends</span> <span class="token class-name">History</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">router</span><span class="token operator">:</span> Router<span class="token punctuation">,</span> base<span class="token operator">?</span><span class="token operator">:</span> string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span>router<span class="token punctuation">,</span> base<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">const</span> expectScroll <span class="token operator">=</span> router<span class="token punctuation">.</span>options<span class="token punctuation">.</span>scrollBehavior<span class="token punctuation">;</span>
    <span class="token keyword">const</span> supportsScroll <span class="token operator">=</span> supportsPushState <span class="token operator">&amp;&amp;</span> expectScroll<span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span>supportsScroll<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">setupScroll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">const</span> initLocation <span class="token operator">=</span> <span class="token function">getLocation</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>base<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 添加事件监听</span>
    window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">&quot;popstate&quot;</span><span class="token punctuation">,</span> <span class="token parameter">e</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> current <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>current<span class="token punctuation">;</span> <span class="token comment">// History路由因为异步防护，不会更新</span>

      <span class="token comment">// 首先避免在某些浏览器中调度第一个`popstate`事件</span>
      <span class="token keyword">const</span> location <span class="token operator">=</span> <span class="token function">getLocation</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>base<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>current <span class="token operator">===</span> <span class="token constant">START</span> <span class="token operator">&amp;&amp;</span> location <span class="token operator">===</span> initLocation<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>

      <span class="token comment">// 该方法进行路由的更新、对应钩子逻辑的执行、处理完毕的回调</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">transitionTo</span><span class="token punctuation">(</span>location<span class="token punctuation">,</span> <span class="token parameter">route</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>supportsScroll<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token function">handleScroll</span><span class="token punctuation">(</span>router<span class="token punctuation">,</span> route<span class="token punctuation">,</span> current<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 前进到某个位置</span>
  <span class="token function">go</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">n</span><span class="token operator">:</span> number</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    window<span class="token punctuation">.</span>history<span class="token punctuation">.</span><span class="token function">go</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 添加到路由</span>
  <span class="token function">push</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">location</span><span class="token operator">:</span> RawLocation<span class="token punctuation">,</span> onComplete<span class="token operator">?</span><span class="token operator">:</span> Function<span class="token punctuation">,</span> onAbort<span class="token operator">?</span><span class="token operator">:</span> Function</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token literal-property property">current</span><span class="token operator">:</span> fromRoute <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
    <span class="token comment">// 前置逻辑执行</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">transitionTo</span><span class="token punctuation">(</span>
      location<span class="token punctuation">,</span>
      <span class="token parameter">route</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token comment">// 添加到历史记录中</span>
        <span class="token function">pushState</span><span class="token punctuation">(</span><span class="token function">cleanPath</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>base <span class="token operator">+</span> route<span class="token punctuation">.</span>fullPath<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">handleScroll</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>router<span class="token punctuation">,</span> route<span class="token punctuation">,</span> fromRoute<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 回调</span>
        onComplete <span class="token operator">&amp;&amp;</span> <span class="token function">onComplete</span><span class="token punctuation">(</span>route<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      onAbort
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 更新当前路由</span>
  <span class="token function">replace</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">location</span><span class="token operator">:</span> RawLocation<span class="token punctuation">,</span> onComplete<span class="token operator">?</span><span class="token operator">:</span> Function<span class="token punctuation">,</span> onAbort<span class="token operator">?</span><span class="token operator">:</span> Function</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token literal-property property">current</span><span class="token operator">:</span> fromRoute <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">transitionTo</span><span class="token punctuation">(</span>
      location<span class="token punctuation">,</span>
      <span class="token parameter">route</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token function">replaceState</span><span class="token punctuation">(</span><span class="token function">cleanPath</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>base <span class="token operator">+</span> route<span class="token punctuation">.</span>fullPath<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">handleScroll</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>router<span class="token punctuation">,</span> route<span class="token punctuation">,</span> fromRoute<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        onComplete <span class="token operator">&amp;&amp;</span> <span class="token function">onComplete</span><span class="token punctuation">(</span>route<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      onAbort
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 确认是否某个路由</span>
  <span class="token function">ensureURL</span><span class="token punctuation">(</span><span class="token parameter">push<span class="token operator">?</span><span class="token operator">:</span> boolean</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">getLocation</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>base<span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token keyword">this</span><span class="token punctuation">.</span>current<span class="token punctuation">.</span>fullPath<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> current <span class="token operator">=</span> <span class="token function">cleanPath</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>base <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>current<span class="token punctuation">.</span>fullPath<span class="token punctuation">)</span><span class="token punctuation">;</span>
      push <span class="token operator">?</span> <span class="token function">pushState</span><span class="token punctuation">(</span>current<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">replaceState</span><span class="token punctuation">(</span>current<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 获取当前路由</span>
  <span class="token function">getCurrentLocation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> string <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">getLocation</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>base<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>我们再来看看 Hash 对象：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">HashHistory</span> <span class="token keyword">extends</span> <span class="token class-name">History</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">router</span><span class="token operator">:</span> Router<span class="token punctuation">,</span> <span class="token literal-property property">base</span><span class="token operator">:</span> <span class="token operator">?</span>string<span class="token punctuation">,</span> <span class="token literal-property property">fallback</span><span class="token operator">:</span> boolean</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span>router<span class="token punctuation">,</span> base<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// check history fallback deeplinking</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>fallback <span class="token operator">&amp;&amp;</span> <span class="token function">checkFallback</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>base<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token function">ensureSlash</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token comment">// 以避免过早地触发hashchange侦听器</span>

  <span class="token comment">// 这会延迟到应用程序安装完毕</span>
  <span class="token function">setupListeners</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>router<span class="token punctuation">;</span>
    <span class="token keyword">const</span> expectScroll <span class="token operator">=</span> router<span class="token punctuation">.</span>options<span class="token punctuation">.</span>scrollBehavior<span class="token punctuation">;</span>
    <span class="token keyword">const</span> supportsScroll <span class="token operator">=</span> supportsPushState <span class="token operator">&amp;&amp;</span> expectScroll<span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span>supportsScroll<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">setupScroll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 添加事件监听</span>
    <span class="token comment">// 优先使用 popstate，同时使用 hashchange 兜底</span>
    window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span>
      supportsPushState <span class="token operator">?</span> <span class="token string">&quot;popstate&quot;</span> <span class="token operator">:</span> <span class="token string">&quot;hashchange&quot;</span><span class="token punctuation">,</span>
      <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> current <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>current<span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">ensureSlash</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token keyword">return</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token comment">// 该方法进行路由的更新、对应钩子逻辑的执行、处理完毕的回调</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">transitionTo</span><span class="token punctuation">(</span><span class="token function">getHash</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token parameter">route</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span>supportsScroll<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token function">handleScroll</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>router<span class="token punctuation">,</span> route<span class="token punctuation">,</span> current<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token punctuation">}</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>supportsPushState<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token function">replaceHash</span><span class="token punctuation">(</span>route<span class="token punctuation">.</span>fullPath<span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 前进到某个位置</span>
  <span class="token function">go</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">n</span><span class="token operator">:</span> number</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    window<span class="token punctuation">.</span>history<span class="token punctuation">.</span><span class="token function">go</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 添加到路由</span>
  <span class="token function">push</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">location</span><span class="token operator">:</span> RawLocation<span class="token punctuation">,</span> onComplete<span class="token operator">?</span><span class="token operator">:</span> Function<span class="token punctuation">,</span> onAbort<span class="token operator">?</span><span class="token operator">:</span> Function</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token literal-property property">current</span><span class="token operator">:</span> fromRoute <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">transitionTo</span><span class="token punctuation">(</span>
      location<span class="token punctuation">,</span>
      <span class="token parameter">route</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token function">pushHash</span><span class="token punctuation">(</span>route<span class="token punctuation">.</span>fullPath<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">handleScroll</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>router<span class="token punctuation">,</span> route<span class="token punctuation">,</span> fromRoute<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        onComplete <span class="token operator">&amp;&amp;</span> <span class="token function">onComplete</span><span class="token punctuation">(</span>route<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      onAbort
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 更新当前路由</span>
  <span class="token function">replace</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">location</span><span class="token operator">:</span> RawLocation<span class="token punctuation">,</span> onComplete<span class="token operator">?</span><span class="token operator">:</span> Function<span class="token punctuation">,</span> onAbort<span class="token operator">?</span><span class="token operator">:</span> Function</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token literal-property property">current</span><span class="token operator">:</span> fromRoute <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">transitionTo</span><span class="token punctuation">(</span>
      location<span class="token punctuation">,</span>
      <span class="token parameter">route</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token function">replaceHash</span><span class="token punctuation">(</span>route<span class="token punctuation">.</span>fullPath<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">handleScroll</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>router<span class="token punctuation">,</span> route<span class="token punctuation">,</span> fromRoute<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        onComplete <span class="token operator">&amp;&amp;</span> <span class="token function">onComplete</span><span class="token punctuation">(</span>route<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      onAbort
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 确认是否某个路由</span>
  <span class="token function">ensureURL</span><span class="token punctuation">(</span><span class="token parameter">push<span class="token operator">?</span><span class="token operator">:</span> boolean</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> current <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>current<span class="token punctuation">.</span>fullPath<span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">getHash</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!==</span> current<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      push <span class="token operator">?</span> <span class="token function">pushHash</span><span class="token punctuation">(</span>current<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">replaceHash</span><span class="token punctuation">(</span>current<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 获取当前路由</span>
  <span class="token function">getCurrentLocation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">getHash</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>我们能看到，在 Vue Router 中路由模式的使用方式被抹平了，不管是 History 模式还是 Hash 模式，最终暴露出来的都是通用的<code>go()</code>、<code>push()</code>、<code>replace()</code>、<code>ensureURL()</code>、<code>getCurrentLocation()</code>这几种方法。而即使在 Hash 模式下，也会优先检测浏览器是否支持 History 模式而使用带&quot;#&quot;的 History 模式，而真正的 Hash 模式则是用来兜底。</p> <p>除此之外，我们能看到还有 Scroll 相关的逻辑，这是 Vue Router 提供的自定义路由切换时页面如何滚动的能力。使用前端路由，当切换到新路由时，想要页面滚到顶部，或者是保持原先的滚动位置，就像重新加载页面那样，可以通过 Router 实例的<code>scrollBehavior</code>方法来定义，这里不详细介绍。我们来看看要怎么使用 Vue Router。</p> <h2 id="_7-2-快速接入-vue-router"><a href="#_7-2-快速接入-vue-router" class="header-anchor">#</a> 7.2 快速接入 Vue Router</h2> <p>前面讲了关于前端路由和实现相关的介绍，大家目前也能大概理解 Vue Router 的功能。从本节内容开始，我们要正式地进入 Vue Router 安装和使用的讲解了。</p> <h3 id="_7-2-1-安装和使用"><a href="#_7-2-1-安装和使用" class="header-anchor">#</a> 7.2.1 安装和使用</h3> <p>跟 Vue 一样，Vue Router 也提供了 CDN 的在线资源。虽然我们也可以使用引入 CDN 资源的方式来进行开发，但是一般来说要用到 Vue Router 的应用都会稍微复杂点，也不适合在一个文件里维护了。所以我们直接介绍依赖安装的方式，同时 Vue CLI 也提供了 vue + vue-router 的快速安装模板，大家也可以去试试看。</p> <h4 id="_1-安装依赖"><a href="#_1-安装依赖" class="header-anchor">#</a> 1. 安装依赖</h4> <p>我们通过 npm 来安装 Vue Router（当然也可以使用 yarn）：</p> <div class="language-cmd extra-class"><pre class="language-text"><code># 安装依赖
npm install vue-router
</code></pre></div><h4 id="_2-引入-vue-router"><a href="#_2-引入-vue-router" class="header-anchor">#</a> 2. 引入 vue-router</h4> <p>在 Vue 中，我们可以通过插件的方式来使用一些工具库、组件系统等。我们需要通过全局方法<code>Vue.use()</code>使用插件（要在调用<code>new Vue()</code>启动应用之前完成）。Vue Router 同样通过插件的方式提供，所以我们可以这样来使用：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// main.js</span>
<span class="token keyword">import</span> Vue <span class="token keyword">from</span> <span class="token string">&quot;vue&quot;</span><span class="token punctuation">;</span>
<span class="token comment">// 引入 vue-router</span>
<span class="token keyword">import</span> VueRouter <span class="token keyword">from</span> <span class="token string">&quot;vue-router&quot;</span><span class="token punctuation">;</span>
Vue<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>VueRouter<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 使用 vue-router</span>
<span class="token comment">// 后面是 new Vue() 相关逻辑</span>
</code></pre></div><p>事实上，Vue.js 官方提供的一些插件(例如这里的 vue-router)，在检测到 Vue 是可访问的全局变量时会自动调用<code>Vue.use()</code>，所以这里其实也可以省略的。</p> <h3 id="_7-2-2-路由设计与配置"><a href="#_7-2-2-路由设计与配置" class="header-anchor">#</a> 7.2.2 路由设计与配置</h3> <p>我们实际开发中，也常常只更新某块内容。像页面顶部的导航条、侧边的菜单列表等等，一般都是固定不变的内容，剩余的部分则需要根据具体的路由情况来渲染对应的页面内容。要实现这样的能力，可以通过嵌套路由或的方式来进行。</p> <p>我们在使用路由功能前，需要先设计一下我们的应用里有哪些页面，例如这是一个简单的路由和嵌套关系：</p> <div class="language-cmd extra-class"><pre class="language-text"><code>/home/page1                /home/page2
+------------------+       +-----------------+
| Home             |       | Home            |
| +--------------+ |       | +-------------+ |
| | Page1        | |       | | Page2       | |
| |              | |       | |             | |
| |              | |  +--) | |             | |
| |              | |       | |             | |
| |              | |       | |             | |
| +--------------+ |       | +-------------+ |
+------------------+       +-----------------+
</code></pre></div><p>为了配对上面的这样一段路由关系，我们需要给 Vue Router 配上路由关系（插件接入的部分已经在前面讲过了）：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// main.js</span>
<span class="token comment">// 前面是使用 Vue Router 插件的部分，省略</span>

<span class="token comment">// 配置路由信息</span>
<span class="token keyword">const</span> routes <span class="token operator">=</span> <span class="token punctuation">[</span>
  <span class="token comment">// 以 / 开头的嵌套路径会被当作根路径</span>
  <span class="token punctuation">{</span>
    <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">&quot;/home&quot;</span><span class="token punctuation">,</span>
    <span class="token literal-property property">component</span><span class="token operator">:</span> Home<span class="token punctuation">,</span>
    <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;Home&quot;</span><span class="token punctuation">,</span>
    <span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token punctuation">[</span>
      <span class="token punctuation">{</span> <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">&quot;page1&quot;</span><span class="token punctuation">,</span> <span class="token literal-property property">component</span><span class="token operator">:</span> Page1<span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;Page1&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span> <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">&quot;page2&quot;</span><span class="token punctuation">,</span> <span class="token literal-property property">component</span><span class="token operator">:</span> Page2<span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;Page2&quot;</span> <span class="token punctuation">}</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token comment">// 通配符 * 会匹配所有路径</span>
  <span class="token comment">// 路由 { path: '*' } 通常用于客户端 404 错误</span>
  <span class="token comment">// 含有通配符的路由应该放在最后</span>
  <span class="token punctuation">{</span> <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">&quot;*&quot;</span><span class="token punctuation">,</span> <span class="token literal-property property">redirect</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;Home&quot;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">VueRouter</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  routes <span class="token comment">// （缩写）相当于 routes: routes</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 启动一个 Vue 应用</span>
<span class="token keyword">new</span> <span class="token class-name">Vue</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token literal-property property">el</span><span class="token operator">:</span> <span class="token string">&quot;#app&quot;</span><span class="token punctuation">,</span>
  router<span class="token punctuation">,</span> <span class="token comment">// 传入路由能力</span>
  <span class="token function-variable function">render</span><span class="token operator">:</span> <span class="token parameter">h</span> <span class="token operator">=&gt;</span> <span class="token function">h</span><span class="token punctuation">(</span>App<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>我们需要在 App 组件中，使用<code>&lt;router-view&gt;</code>添加最顶层的出口，渲染最高级路由匹配到的组件：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token comment">&lt;!-- App.vue --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token comment">&lt;!-- 使用 &lt;router-view&gt;&lt;/router-view&gt; 来渲染最高级路由匹配到的组件 --&gt;</span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-view</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-view</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p>同时，我们需要在 Home 页面中使用<code>&lt;router-view&gt;</code>来展示子路由界面：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token comment">&lt;!-- Home.vue --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> <span class="token attr-name">:to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>{ name: <span class="token punctuation">'</span>Page1<span class="token punctuation">'</span>}<span class="token punctuation">&quot;</span></span> <span class="token attr-name">tag</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>button<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>goto Page1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> <span class="token attr-name">:to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>{ name: <span class="token punctuation">'</span>Page2<span class="token punctuation">'</span>}<span class="token punctuation">&quot;</span></span> <span class="token attr-name">tag</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>button<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>goto Page2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token comment">&lt;!-- 子路由界面 --&gt;</span>
    <span class="token comment">&lt;!-- 如果路由为 /home/page1，此处是 Page1 组件 --&gt;</span>
    <span class="token comment">&lt;!-- 如果路由为 /home/page2，此处是 Page2 组件 --&gt;</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-view</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-view</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p><code>&lt;router-view&gt;</code>其实可以理解为占位符，而占位的内容是匹配到的路由信息对应的组件，这个组件会替代<code>&lt;router-view&gt;</code>进行渲染。一个被渲染组件同样可以包含自己的嵌套<code>&lt;router-view&gt;</code>，我们只需要保证在<code>routes</code>中的路由配置信息和我们的嵌套关系保持一致就可以。</p> <p>这里还出现了<code>&lt;router-link&gt;</code>元素，它是 Vue Router 中的一种导航方式，可以理解为加了路由能力的<code>&lt;a&gt;</code>标签。</p> <blockquote><p><a href="https://vue-eboook-1255459943.cos.ap-chengdu.myqcloud.com/7/1-vue-router-init/index.html" target="_blank" rel="noopener noreferrer">点击此处查看页面效果<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> <a href="https://github.com/godbasin/vue-ebook/tree/vue-sourcecode/7/1-vue-router-init" target="_blank" rel="noopener noreferrer">点击此处查看源码<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p></blockquote> <h4 id="路由配置参数"><a href="#路由配置参数" class="header-anchor">#</a> 路由配置参数</h4> <p>匹配到对应路由的时候该加载什么组件、是否使用了路由钩子、是否要重定向等，都可以通过<code>routes</code>来配置。我们来看看路由配置<code>routes</code>的参数都包括哪些：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">interface</span> <span class="token class-name">RouteConfig</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">path</span><span class="token operator">:</span> string<span class="token punctuation">,</span> <span class="token comment">// 匹配的路径</span>
  component<span class="token operator">?</span><span class="token operator">:</span> Component<span class="token punctuation">,</span> <span class="token comment">// 命中的组件，会加载在父组件/根页面下的 &lt;router-view&gt; 所在位置</span>
  name<span class="token operator">?</span><span class="token operator">:</span> string<span class="token punctuation">,</span> <span class="token comment">// 命名路由，相当于给路由起个名字</span>
  components<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">[</span>name<span class="token operator">:</span> string<span class="token punctuation">]</span><span class="token operator">:</span> Component <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment">// 命名视图组件，需要渲染多个组件的时候可以使用</span>
  redirect<span class="token operator">?</span><span class="token operator">:</span> string <span class="token operator">|</span> Location <span class="token operator">|</span> Function<span class="token punctuation">,</span> <span class="token comment">// 重定向到某个路由</span>
  props<span class="token operator">?</span><span class="token operator">:</span> boolean <span class="token operator">|</span> Object <span class="token operator">|</span> Function<span class="token punctuation">,</span>
  alias<span class="token operator">?</span><span class="token operator">:</span> string <span class="token operator">|</span> Array<span class="token operator">&lt;</span>string<span class="token operator">&gt;</span><span class="token punctuation">,</span>
  children<span class="token operator">?</span><span class="token operator">:</span> Array<span class="token operator">&lt;</span>RouteConfig<span class="token operator">&gt;</span><span class="token punctuation">,</span> <span class="token comment">// 嵌套路由，像 routes 配置一样的路由配置数组</span>
  beforeEnter<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">to</span><span class="token operator">:</span> Route<span class="token punctuation">,</span> <span class="token literal-property property">from</span><span class="token operator">:</span> Route<span class="token punctuation">,</span> <span class="token literal-property property">next</span><span class="token operator">:</span> Function</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">,</span> <span class="token comment">// 进入路由的钩子</span>
  meta<span class="token operator">?</span><span class="token operator">:</span> any<span class="token punctuation">,</span>

  <span class="token comment">// 2.6.0+</span>
  caseSensitive<span class="token operator">?</span><span class="token operator">:</span> boolean<span class="token punctuation">,</span> <span class="token comment">// 匹配规则是否大小写敏感？(默认值：false)</span>
  pathToRegexpOptions<span class="token operator">?</span><span class="token operator">:</span> Object <span class="token comment">// 编译正则的选项</span>
<span class="token punctuation">}</span>
</code></pre></div><p>我们经常会使用的配置主要包括<code>path</code>、<code>component</code>、<code>name</code>、<code>redirect</code>、<code>children</code>等，更多的使用方式可以参考官网的说明。</p> <h3 id="_7-2-3-导航方式"><a href="#_7-2-3-导航方式" class="header-anchor">#</a> 7.2.3 导航方式</h3> <p>在 Vue Router 中，有两种导航方式：<br>
(1) <code>router</code>编程式导航。<br>
(2) <code>&lt;router-link&gt;</code>声明式导航。</p> <p>我们分别来看一下。</p> <h4 id="编程式导航"><a href="#编程式导航" class="header-anchor">#</a> 编程式导航</h4> <p>Vue Router 提供了<code>router</code>的实例方法，通过编写代码来实现导航功能。在 Vue 实例内部，你可以通过<code>$router</code>访问路由实例，我们来看看该实例提供的一些方法。</p> <p><strong>router.push</strong><br>
想要导航到不同的 URL，则使用<code>router.push</code>方法。这个方法会向 history 栈添加一个新的记录，所以，当用户点击浏览器后退按钮时，则回到之前的 URL。使用方式如下：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 字符串</span>
router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token string">&quot;/home&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 对象</span>
router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">&quot;/home&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 命名的路由</span>
router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;Home&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p><strong>router.replace</strong><br>
跟<code>router.push</code>很像，唯一的不同就是，它不会向 history 添加新记录，而会替换掉当前的 history 记录。</p> <p><strong>router.go(n)</strong><br>
这个方法的参数是一个整数，意思是在 history 记录中向前（正整数）或者后退（负整数）多少步，类似<code>window.history.go(n)</code>:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 在浏览器记录中前进一步，等同于 history.forward()</span>
router<span class="token punctuation">.</span><span class="token function">go</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 后退一步记录，等同于 history.back()</span>
router<span class="token punctuation">.</span><span class="token function">go</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>前面我们有讲到，Hash 模式和 History 模式的路由方法差异被封装和抹平了，所以 Vue Router 的导航方法(<code>push</code>、<code>replace</code>、<code>go</code>)在各类路由模式 mode(<code>history</code>、<code>hash</code>和<code>abstract</code>)下表现一致。</p> <h4 id="声明式导航"><a href="#声明式导航" class="header-anchor">#</a> 声明式导航</h4> <p><code>&lt;router-link&gt;</code>组件支持用户在具有路由功能的应用中 (点击) 导航。通过<code>to</code>属性指定目标地址，默认渲染成带有正确链接的<code>&lt;a&gt;</code>标签，可以通过配置<code>tag</code>属性生成别的标签。另外，当目标路由成功激活时，链接元素自动设置一个表示激活的 CSS 类名。</p> <p>表 7-3 <code>&lt;router-link&gt;</code>Props 说明</p> <table><thead><tr><th><code>&lt;router-link&gt;</code>Props</th> <th>说明</th> <th>类型</th></tr></thead> <tbody><tr><td><code>to</code></td> <td>表示目标路由的链接。当被点击后，内部会立刻把<code>to</code>的值传到<code>router.push()</code>，所以这个值可以是一个字符串或者是描述目标位置的对象</td> <td><code>string</code>/<code>Location</code></td></tr> <tr><td><code>replace</code></td> <td>当点击时，会调用<code>router.replace()</code>而不是<code>router.push()</code>，于是导航后不会留下 history 记录</td> <td><code>boolean</code>（默认<code>false</code>）</td></tr> <tr><td><code>tag</code></td> <td>指定<code>&lt;router-link&gt;</code>渲染成某种标签。同样它还是会监听点击，触发导航</td> <td><code>string</code>（默认<code>&quot;a&quot;</code>）</td></tr> <tr><td><code>active-class</code></td> <td>设置链接激活时使用的 CSS 类名</td> <td><code>string</code>（默认<code>&quot;router-link-active&quot;</code>）</td></tr> <tr><td><code>event</code></td> <td>声明可以用来触发导航的事件</td> <td><code>string</code>/<code>Array&lt;string&gt;</code>（默认<code>&quot;click&quot;</code>）</td></tr></tbody></table> <p>以上这些属性是比较常用的，我们可以看一些代码来体验一下：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token comment">&lt;!-- 字符串 --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/home<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
<span class="token comment">&lt;!-- 使用 v-bind 的 JS 表达式 --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> <span class="token attr-name"><span class="token namespace">v-bind:</span>to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span><span class="token punctuation">'</span>/home<span class="token punctuation">'</span><span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
<span class="token comment">&lt;!-- 不写 v-bind 也可以，就像绑定别的属性一样 --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> <span class="token attr-name">:to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span><span class="token punctuation">'</span>/home<span class="token punctuation">'</span><span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
<span class="token comment">&lt;!-- 可使用 router.push() 的参数 --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> <span class="token attr-name">:to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>{ name: <span class="token punctuation">'</span>Home<span class="token punctuation">'</span> }<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>

<span class="token comment">&lt;!-- router-link 替换元素为 button --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> <span class="token attr-name">:to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>{ name: <span class="token punctuation">'</span>Home<span class="token punctuation">'</span> }<span class="token punctuation">&quot;</span></span> <span class="token attr-name">tag</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>button<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
<span class="token comment">&lt;!-- 渲染结果 --&gt;</span>
<span class="token comment">&lt;!-- 不会有 herf 内容，但会监听点击，触发导航 --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span><span class="token punctuation">&gt;</span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> <span class="token attr-name">:to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>{ name: <span class="token punctuation">'</span>Page1<span class="token punctuation">'</span> }<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>Page1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
<span class="token comment">&lt;!-- 当当前的路由命中时，router-link-exact-active 为精确匹配时的样式 --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>#/home/page1<span class="token punctuation">&quot;</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>router-link-exact-active router-link-active<span class="token punctuation">&quot;</span></span>
  <span class="token punctuation">&gt;</span></span>Page1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span>
<span class="token punctuation">&gt;</span></span>
</code></pre></div><h3 id="_7-2-4-路由传参"><a href="#_7-2-4-路由传参" class="header-anchor">#</a> 7.2.4 路由传参</h3> <p>有些时候，我们需要在 URL 上带一些参数来标识当前内容。因为页面逻辑是通用的，只有内容不一样，这些内容的数据常常是根据某个 ID 来从后台获取，例如根据 ID 从后台获取某本书的详细信息。如果我们希望刷新页面的时候该 ID 不会丢失，则需要把这个标识带到我们的 URL 里。通常我们有两种方式来携带：<br>
(1) <code>/page/detail?id=123</code>，在 Vue Router 中用<code>query</code>表示。<br>
(2) <code>/page/detail/123</code>，在 Vue Router 中用<code>params</code>表示。</p> <p>通常来说，我们需要解析 URL 来获取对应的<code>query</code>或者<code>params</code>参数。我们来看看在 Vue Router 中，是怎样解析<code>query</code>的：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">parsePath</span><span class="token punctuation">(</span>
  <span class="token parameter"><span class="token literal-property property">path</span><span class="token operator">:</span> string</span>
<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">path</span><span class="token operator">:</span> string<span class="token punctuation">,</span>
  <span class="token literal-property property">query</span><span class="token operator">:</span> string<span class="token punctuation">,</span>
  <span class="token literal-property property">hash</span><span class="token operator">:</span> string
<span class="token punctuation">}</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> hash <span class="token operator">=</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">;</span>
  <span class="token keyword">let</span> query <span class="token operator">=</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">;</span>

  <span class="token comment">// 解析 hash 片段</span>
  <span class="token keyword">const</span> hashIndex <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">&quot;#&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>hashIndex <span class="token operator">&gt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    hash <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span>hashIndex<span class="token punctuation">)</span><span class="token punctuation">;</span>
    path <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> hashIndex<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 解析 query 片段</span>
  <span class="token keyword">const</span> queryIndex <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">&quot;?&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>queryIndex <span class="token operator">&gt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    query <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span>queryIndex <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    path <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> queryIndex<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">return</span> <span class="token punctuation">{</span> path<span class="token punctuation">,</span> query<span class="token punctuation">,</span> hash <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>我们能看到，通过<code>?</code>来分割出 URL 中的<code>query</code>片段，然后再进行处理就能获得所有的<code>query</code>参数和传参内容，而其实<code>params</code>的实现方式也是类似的。我们来分别看看两种方式要怎么传参和获取参数值。</p> <h4 id="params-传参"><a href="#params-传参" class="header-anchor">#</a> params 传参</h4> <p>params 的传参模式，我们需要首先在路由配置中进行特殊的配置（冒号&quot;:&quot;标记）：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">VueRouter</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token literal-property property">routes</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">&quot;/home&quot;</span><span class="token punctuation">,</span>
      <span class="token literal-property property">component</span><span class="token operator">:</span> Home<span class="token punctuation">,</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;Home&quot;</span><span class="token punctuation">,</span>
      <span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token punctuation">[</span>
        <span class="token comment">// 动态路径参数以冒号 &quot;:&quot; 开头</span>
        <span class="token punctuation">{</span> <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">&quot;page1/:id&quot;</span><span class="token punctuation">,</span> <span class="token literal-property property">component</span><span class="token operator">:</span> Page1<span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;Page1&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span> <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">&quot;page2&quot;</span><span class="token punctuation">,</span> <span class="token literal-property property">component</span><span class="token operator">:</span> Page2<span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;Page2&quot;</span> <span class="token punctuation">}</span>
      <span class="token punctuation">]</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>一个“路径参数”(param)使用冒号<code>:</code>标记。当匹配到一个路由时，参数值会被设置到<code>$route.params</code>，可以在每个组件内使用：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token comment">&lt;!-- Page1.vue --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token comment">&lt;!-- $route 可直接注入到模板 --&gt;</span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>{{ $route.params.id }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">&gt;</span></span><span class="token script"><span class="token language-javascript">
  <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
    <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 每个组件中都可以通过 this.$route 获取路由信息</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>$route<span class="token punctuation">.</span>params<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p>而我们在跳转的时候，可以通过编程式导航或声明式导航来传参：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 编程式导航传参</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>$router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;Page1&quot;</span><span class="token punctuation">,</span> <span class="token literal-property property">params</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token number">123</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 会跳转到 /home/page1/123</span>
</code></pre></div><div class="language-html extra-class"><pre class="language-html"><code><span class="token comment">&lt;!-- 声明式导航传参 --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> <span class="token attr-name">:to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>{ name: <span class="token punctuation">'</span>Page1<span class="token punctuation">'</span>, params: {id: 1234}}<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>goto Page1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
<span class="token comment">&lt;!-- 点击会跳转到 /home/page1/1234 --&gt;</span>
</code></pre></div><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-7-1.jpg" alt="image"><br>
图 7-1 params 传参效果</p> <h4 id="query-传参"><a href="#query-传参" class="header-anchor">#</a> query 传参</h4> <p>params 传参有一个不方便的地方，即我们必须要传入一个动态路径参数才能匹配到对应的页面。但是有些时候，我们的页面也允许某些可选参数的缺失，例如我们想要<code>/edit</code>表示新建，<code>/edit/123</code>表示修改<code>id=123</code>的内容，但是当我们输入<code>/edit</code>的时候却匹配不到相同的页面。这种时候我们可以使用 query 传参，<code>/edit</code>表示新建、<code>/edit?id=123</code>表示修改。</p> <p>query 的传参模式，我们不需要像 params 传参一样使用<code>/xxxx/:xxx</code>这样更改路由配置，只需要在导航的时候传参：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 编程式导航传参</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>$router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;Page2&quot;</span><span class="token punctuation">,</span> <span class="token literal-property property">query</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token number">123</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 会跳转到 /home/page2?id=123</span>
</code></pre></div><div class="language-html extra-class"><pre class="language-html"><code><span class="token comment">&lt;!-- 声明式导航传参 --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> <span class="token attr-name">:to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>{ name: <span class="token punctuation">'</span>Page2<span class="token punctuation">'</span>, query: {id: 1234}}<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>goto Page2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
<span class="token comment">&lt;!-- 点击会跳转到 /home/page2?id=1234 --&gt;</span>
</code></pre></div><p>在使用这样的方式导航之后，参数值会被设置到<code>$route.query</code>，同样支持在每个组件内使用：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token comment">&lt;!-- Page2.vue --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token comment">&lt;!-- $route 可直接注入到模板 --&gt;</span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>{{ $route.query.id }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">&gt;</span></span><span class="token script"><span class="token language-javascript">
  <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
    <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 每个组件中都可以通过 this.$route 获取路由信息</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>$route<span class="token punctuation">.</span>query<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-7-2.jpg" alt="image"><br>
图 7-2 query 传参效果</p> <blockquote><p><a href="https://vue-eboook-1255459943.cos.ap-chengdu.myqcloud.com/7/2-vue-params-and-query/index.html" target="_blank" rel="noopener noreferrer">点击此处查看页面效果<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> <a href="https://github.com/godbasin/vue-ebook/tree/vue-sourcecode/7/2-vue-params-and-query" target="_blank" rel="noopener noreferrer">点击此处查看源码<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p></blockquote> <p>以上，我们介绍了 Vue Router 的安装、注入、嵌套路由、路由导航和传参，基本上已经覆盖了大多数基础的 web 应用路由能力。但是在一些特殊的场景下，我们还需要更多的能力如路由拦截、监听、懒加载等，还需要进一步地掌握更多的使用方法。</p> <h2 id="_7-3-vue-router-进阶"><a href="#_7-3-vue-router-进阶" class="header-anchor">#</a> 7.3 Vue Router 进阶</h2> <p>Vue Router 进阶主要讲述一些稍微偏门但是也经常需要使用到的路由能力，包括路由监听、鉴权、路由懒加载等。</p> <h3 id="_7-3-1-监听路由"><a href="#_7-3-1-监听路由" class="header-anchor">#</a> 7.3.1 监听路由</h3> <p>当我们使用单个组件绑定多个路由（或者多个路由参数）的时候，路由的跳转并不能再次触发组件的重建（因此无法重新触发一些生命周期钩子），此时我们需要对路由进行监视。例如，我们对某个页面进行传参，来控制是需要新建某个内容，还是需要修改某项内容。</p> <p>现在，我们有一个修改的路由<code>/edit</code>和一个新建的路由<code>/add</code>，但两个路由其实是匹配到同一个组件，因为页面内容是相似的。一般来说我们要对某个表单内容进行修改，需要根据当前的 id（这个 id 通常保存在 URL 路由中）从后台拉取已有的内容填充，然后再对这些内容进行修改。</p> <p>如果我们在修改某个内容的时候（此时在<code>/edit</code>路由下），又通过<code>&lt;router-link&gt;</code>或别的方式触发到新增（跳转到<code>/add</code>路由）。由于两个路由匹配到了同一个组件，组件不会重新进行渲染，原先已填充的内容依然存在，即使它是需要清空或更新的。</p> <p>这个时候，我们可以通过<code>watch</code>(监测变化)<code>$route</code>对象，来对路由参数的变化作出响应：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>Detail<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>{{$route.query.id ? '修改' : '新建'}}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>name: <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">v-model</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>detail.name<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>text: <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">v-model</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>detail.text<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">&gt;</span></span><span class="token script"><span class="token language-javascript">
  <span class="token comment">// 下面是 Vue 组件</span>
  <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
    <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token punctuation">{</span>
        <span class="token literal-property property">detail</span><span class="token operator">:</span> <span class="token punctuation">{</span>
          <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">,</span>
          <span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">&quot;&quot;</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">watch</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token function">$route</span><span class="token punctuation">(</span><span class="token parameter">to<span class="token punctuation">,</span> <span class="token keyword">from</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 对路由变化作出响应，更新参数</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">updateDetail</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token function">updateDetail</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> id <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$route<span class="token punctuation">.</span>query<span class="token punctuation">.</span>id<span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>id<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token comment">// 传入 id 则意味着修改，需要获取并录入原先内容</span>
          <span class="token keyword">this</span><span class="token punctuation">.</span>detail <span class="token operator">=</span> <span class="token punctuation">{</span>
            <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">name-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
            <span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">text-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
          <span class="token punctuation">}</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
          <span class="token comment">// 未传入 id 则意味着新建，需要重置原有内容</span>
          <span class="token keyword">this</span><span class="token punctuation">.</span>detail <span class="token operator">=</span> <span class="token punctuation">{</span>
            <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">,</span>
            <span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">&quot;&quot;</span>
          <span class="token punctuation">}</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-7-5.jpg" alt="image"><br>
图 7-3 路由参数 watch 效果</p> <p>如图 7-3，我们可以通过 watch <code>$route</code>，在每次路由更新之后，重新获取 id 然后更新对应的内容。但上面这种做法依然存在问题，如果我们的路由从<code>detail?id=123</code>变成了<code>detail?id=123&amp;test=hahaha</code>，<code>$route</code>会触发侦听器，但是我们的 id 其实并没有变更，而这个时候由于重新获取内容，会覆盖掉我们正在编辑的内容。为了避免这种情况，我们可以通过参数<code>(to, from)</code>来检测是否不一致：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">watch</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token function">$route</span><span class="token punctuation">(</span><span class="token parameter">to<span class="token punctuation">,</span> <span class="token keyword">from</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 对路由变化作出响应</span>
      <span class="token comment">// 只有 id 值变更的时候，才进行更新</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>to<span class="token punctuation">.</span>query<span class="token punctuation">.</span>id <span class="token operator">!==</span> from<span class="token punctuation">.</span>query<span class="token punctuation">.</span>id<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">updateDetail</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><blockquote><p><a href="https://vue-eboook-1255459943.cos.ap-chengdu.myqcloud.com/7/3-vue-watch-route/index.html" target="_blank" rel="noopener noreferrer">点击此处查看页面效果<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> <a href="https://github.com/godbasin/vue-ebook/tree/vue-sourcecode/7/3-vue-watch-route" target="_blank" rel="noopener noreferrer">点击此处查看源码<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p></blockquote> <p>以上，便是 Vue Router 提供的路由监听能力，也是很实用的。</p> <h3 id="_7-3-2-路由鉴权"><a href="#_7-3-2-路由鉴权" class="header-anchor">#</a> 7.3.2 路由鉴权</h3> <p>Vue Router 提供了导航守卫的功能，主要用来通过跳转或取消的方式守卫导航。守卫导航可以理解为，我们需要进入某个路由页面，而守卫导航的职责是负责检查我们有没有通行证，有就放行，没有就进行拦截。导航守卫经常用在权限和条件控制中，例如管理者的某个操作界面/路由，只有拥有管理者权限的人才能进入。这个过程我们简单称作路由鉴权。</p> <p>常见的，例如我们的管理系统需要登录才能进入，否则默认跳转到登录页面：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">VueRouter</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token comment">// 这里我们注册一个前置守卫</span>
router<span class="token punctuation">.</span><span class="token function">beforeEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">to<span class="token punctuation">,</span> from<span class="token punctuation">,</span> next</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>to<span class="token punctuation">.</span>name <span class="token operator">!==</span> <span class="token string">&quot;Login&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 非 login 页面，检查是否登录</span>
    <span class="token comment">// 这里简单前端模拟是否填写了用户名，真实环境需要走后台登录，缓存到本地</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>isLogin<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 未登录则需要跳转到登录页面</span>
      <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&quot;Login&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 其他情况正常执行</span>
  <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>我们能看到，当用户去往的页面并非登录页面、同时该用户属于未登录状态的情况下，守卫会将用户转到登录页面，其他情况下正常访问。</p> <p>在这里，<code>beforeEach</code>是 Vue Router 的全局前置守卫，而在 Vue Router 中还有全局解析守卫<code>beforeResolve</code>、全局后置守卫<code>afterEach</code>、路由配置的<code>beforeEnter</code>前置守卫、每个组件都拥有的守卫（<code>beforeRouteEnter</code>、<code>beforeRouteUpdate</code>、<code>beforeRouteLeave</code>），每个守卫（除了<code>afterEach</code>）的参数都是一样的<code>(to, from, next)</code>：</p> <p>表 7-4 Vue Router 守卫参数名</p> <table><thead><tr><th>参数名</th> <th>说明</th></tr></thead> <tbody><tr><td><code>to</code></td> <td>即将要进入的目标 Route 路由对象</td></tr> <tr><td><code>from</code></td> <td>当前导航正要离开的 Route 路由对象</td></tr> <tr><td><code>next</code></td> <td>一定要调用该方法来 resolve 这个钩子</td></tr></tbody></table> <p><code>next</code>的使用方式如下：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 进行管道中的下一个钩子</span>
<span class="token comment">// 如果全部钩子执行完了，则导航的状态就是confirmed(确认的)</span>
<span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token comment">// 中断当前的导航</span>
<span class="token comment">// 如果浏览器的 URL 改变了，那么 URL 地址会重置到 from 路由对应的地址</span>
<span class="token function">next</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span>

<span class="token comment">// 当前的导航被中断，然后进行一个新的导航，跳转到一个不同的地址</span>
<span class="token comment">// 可以向 next 传递任意位置 router.push 中的选项</span>
<span class="token function">next</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span>
<span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">'/'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token comment">// (2.4.0+) 终止导航</span>
<span class="token comment">// 传入一个 Error 实例，导航会被终止且该错误会被传递给 router.onError() 注册过的回调</span>
<span class="token function">next</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token operator">:</span>
</code></pre></div><p>守卫是异步解析执行，此时导航在所有守卫<code>resolve</code>完之前一直处于等待中。关于不同的守卫注意事项这里不详细介绍了，一般来说我们只需要大概有哪些能力，需要用的时候再去查。大家可以去官网上看，但是完整的导航解析流程可以简单了解下：</p> <p>表 7-5 完整的导航解析流程</p> <table><thead><tr><th>序号</th> <th>流程</th> <th>说明</th></tr></thead> <tbody><tr><td>1</td> <td>导航被触发</td> <td>-</td></tr> <tr><td>2</td> <td>在失活的组件里调用<code>beforeRouteLeave</code>离开守卫</td> <td><code>beforeRouteLeave</code>: 导航离开该组件的对应路由时调用</td></tr> <tr><td>3</td> <td>调用全局的<code>beforeEach</code>守卫</td> <td><code>beforeEach</code>: 当一个导航触发时，全局前置守卫按照创建顺序调用</td></tr> <tr><td>4</td> <td>在重用的组件里调用<code>beforeRouteUpdate</code>守卫</td> <td><code>beforeRouteUpdate</code>: 在当前路由改变，但是该组件被复用时调用</td></tr> <tr><td>5</td> <td>在路由配置里调用<code>beforeEnter</code></td> <td>如果路由配置<code>route</code>里有设置，则会调用</td></tr> <tr><td>6</td> <td>解析异步路由组件</td> <td>-</td></tr> <tr><td>7</td> <td>在被激活的组件里调用<code>beforeRouteEnter</code></td> <td><code>beforeRouteEnter</code>: 在渲染该组件的对应路由被 confirm 前调用</td></tr> <tr><td>8</td> <td>调用全局的<code>beforeResolve</code>守卫</td> <td><code>beforeResolve</code>: 在导航被确认之前，同时在所有组件内守卫和异步路由组件被解析之后，解析守卫就被调用</td></tr> <tr><td>9</td> <td>导航被确认，导航状态为 confirmed</td> <td>-</td></tr> <tr><td>10</td> <td>调用全局的<code>afterEach</code>钩子</td> <td><code>afterEach</code>钩子不会接受<code>next</code>函数也不会改变导航本身</td></tr> <tr><td>11</td> <td>触发 DOM 更新</td> <td>-</td></tr> <tr><td>12</td> <td>用创建好的实例调用<code>beforeRouteEnter</code>守卫中传给<code>next</code>的回调</td> <td>-</td></tr></tbody></table> <p>需要注意的是，参数<code>params</code>或查询<code>query</code>的改变并不会触发进入/离开的导航守卫，两种传参的改变只能通过前面介绍的 watch <code>$route</code>来获取。</p> <h3 id="_7-3-3-路由懒加载"><a href="#_7-3-3-路由懒加载" class="header-anchor">#</a> 7.3.3 路由懒加载</h3> <p>懒加载是前端性能优化中很重要的一个能力。懒加载的出现，是因为我们使用工具进行代码打包，而打包过程会把整个应用的逻辑都打包到一起，这种情况下 JavaScript 包会变得非常大，影响页面加载。</p> <p>Vue Router 提供了很简单的配置方式，来允许我们把不同路由对应的组件分割成不同的代码块。当对应的路由被访问的时候，Vue Router 才会加载对应组件，这样就能大大减小首页的代码包大小，加快加载速度。使用方式是：<br>
(1) 将异步组件定义为返回一个 Promise 的工厂函数，该函数返回的 Promise 需要 resolve 组件本身。<br>
(2) 使用动态<code>import</code>语法来定义代码分块点（依赖了 Webpack 的代码分割功能）。</p> <p>默认官方脚手架 Vue CLI 使用的是 Babel，我们需要添加<code>syntax-dynamic-import</code>插件，才能使 Babel 可以正确地解析语法，进行代码分割。简单来说，就是这样：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 实现 Page2 页面懒加载</span>
<span class="token comment">// 不会被打包到主包中，当匹配到对应的路由时候，才会被加载</span>
<span class="token keyword">const</span> <span class="token function-variable function">Page2</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">&quot;./Page2.vue&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>如果我们需要把几个组件都打包到一起，使用相同的<code>webpackChunkName</code>就可以实现：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">Page1</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token comment">/* webpackChunkName: &quot;page&quot; */</span> <span class="token string">&quot;./Page1.vue&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token function-variable function">Page2</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token comment">/* webpackChunkName: &quot;page&quot; */</span> <span class="token string">&quot;./Page2.vue&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>到这里，我们本章的内容也结束了。从路由的出现、实现方法，到 Vue Router 的常用能力介绍，相信大家对 Web 应用的页面渲染过程、URL 路由解析也多了不少的了解，而掌握了 Vue Router 的大致实现和功能，我们也能很快地找到一些问题的解决办法。</p></div> <!----> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/godbasin/vue-ebook/edit/ebook-sourcecode/docs/vue-ebook/7.md" target="_blank" rel="noopener noreferrer">帮阿猪改善此页面！</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <!----> <div style="margin-top:30px;"><div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-sm-0 el-col-md-2 el-col-lg-4" style="padding-left:10px;padding-right:10px;display:block;"><div style="width:1px;height:1px;"></div></div> <div class="el-col el-col-24 el-col-sm-24 el-col-md-18 el-col-lg-16" style="padding-left:10px;padding-right:10px;"><div class="el-card box-card is-always-shadow"><div class="el-card__header"><div class="clearfix"><span>温馨提示喵</span></div></div><div class="el-card__body"> <div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="el-image"><div class="image-slot"><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/loading.gif" style="width:100%;"></div><!----></div></div> <div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="copyright-text"><div>本书采用“保持署名—非商用”<a href="http://creativecommons.org/licenses/by-nc/4.0/" target="_blank">创意共享4.0许可证</a>。只要保持原作者署名和非商用，您可以自由地阅读、分享、修改本书。</div> <div>作者：<a href="https://github.com/godbasin" target="_blank">被删</a></div></div></div></div></div></div></div></div></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
        ←
        <a href="/vue-ebook/vue-ebook/6.html" class="prev">
          第6章 Vue 动画
        </a></span> <span class="next"><a href="/vue-ebook/vue-ebook/8.html">
          第8章 实战：Todo List 从组件到应用
        </a>
        →
      </span></p></div>  <div class="gitalk-container theme-default-content"><div id="gitalk-container" class="content"></div></div></main> <div id="kitty-container"><span><div role="tooltip" id="el-popover-6785" aria-hidden="true" class="el-popover el-popper" style="width:undefinedpx;display:none;"><!----><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/2code2.jpg" class="image"> <div class="text">牡羊猪的猫粮罐</div> </div><span class="el-popover__reference-wrapper"><div id="kitty" style="background:url(https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/kitty2.svg);"></div></span></span> <div class="el-dialog__wrapper" style="display:none;"><div role="dialog" aria-modal="true" aria-label="牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）" class="el-dialog" style="margin-top:15vh;"><div class="el-dialog__header"><span class="el-dialog__title">牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）</span><button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close"></i></button></div><!----><!----></div></div></div></div><div class="global-ui"></div></div>
    <script src="/vue-ebook/assets/js/app.a856ee15.js" defer></script><script src="/vue-ebook/assets/js/2.2a4d101b.js" defer></script><script src="/vue-ebook/assets/js/3.9083e877.js" defer></script><script src="/vue-ebook/assets/js/21.15bf7d53.js" defer></script>
  </body>
</html>
